import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferStrategy;
import java.util.ArrayList;

public class Game extends Frame implements GameInterface, MouseListener, MouseMotionListener, KeyListener {

	private Phase currentPhase;
	
	private long cycleduration;
	private long precycle, postcycle;
	
	private String name;
	
	private BufferStrategy strategy;
	private int width, depth;
	private int x, y;
	
	private ImageLoader il;
	
	private ArrayList <InputEvent> ieal;
	private KeyEvent last;

	public Game(){	
		hardcode();
		this.setTitle(name);
		this.setResizable(false);
		this.setVisible(true);
		setFocusTraversalKeysEnabled(false);
		this.requestFocus();
		x = this.getInsets().left;
		y = this.getInsets().top; 
		this.setBounds(0,0,width + x + this.getInsets().right, depth + y + this.getInsets().bottom);
		this.addWindowListener (new WindowAdapter()
		{
			public void windowClosing (WindowEvent e)
			{
				endProgram();
			}
		});
		il = new ImageLoader();
		currentPhase = new SetupPhase(this);
		ieal = new ArrayList<InputEvent>();
		this.addKeyListener(this);
		this.addMouseListener(this);
		this.addMouseMotionListener(this);
		this.createBufferStrategy(2);
		strategy = this.getBufferStrategy();
		//System.out.println(this.getMaximizedBounds().toString());
	}
	
	// method hardcode hard-codes specific variables
	public void hardcode() {
		name = "Space Elements";
	
		width = 760;
		depth = 570;
	
		last = null;
	
		precycle = 0;
		postcycle = 0;
		cycleduration = 0;
	}
	
	// method run contains the main game loop
	public void run() {
		precycle = System.nanoTime();
		while(true)
		{
			update();
			manage();
		}
	}
	
	// method update updates the game
	public void update() {
		changeInputEventList(null);
		currentPhase.update(cycleduration);
		display();
	}
	// method display displays the graphics
	public void display() {
		Graphics2D g = (Graphics2D)(strategy.getDrawGraphics());
		g.setColor(Color.WHITE);
		g.fillRect(x, y, width, depth);
		if(currentPhase != null)
		{
			currentPhase.display(g);
			//System.out.println("getting here");
		}
		strategy.show();
	}
	// method manage calculates the time elapsed for each cycle
	public void manage() {
		postcycle = System.nanoTime();
		cycleduration = 0 + postcycle - precycle;
		precycle = 0 + postcycle;
		double temp = (double)(cycleduration)/1000000000;
		//System.out.println(temp);
	}
	
	// method changeInputEventList synchronizes all input with the main loop
	public synchronized void changeInputEventList(InputEvent ciel) {
		if(ciel != null)
			ieal.add(ciel);
		else
		{
			for(int b=0;b<ieal.size();b++)
			{
				if(ieal.get(b).getID() > 500)
				{
					//System.out.println(((MouseEvent)(ie.get(b))).toString());
					currentPhase.mouseInput((MouseEvent)(ieal.get(b)));
				}
				else
				{
					currentPhase.keyInput((KeyEvent)(ieal.get(b)));
				}
			}
			ieal.clear();
		}
	}
	
	// method endProgram makes the frame become not visible, then ends the program
	public void endProgram() {
		this.setVisible(false);
		System.exit(0);
	}
	
	public ImageLoader getImageLoader(){return il;}
	public void quit(){endProgram();}
	public Phase getCurrentPhase(){return currentPhase;}
	public void setCurrentPhase(Phase scp){currentPhase = scp;}
	public Point getInsetAdjustment(){return new Point(x,y);}
	public Dimension getDimensions(){return new Dimension(width,depth);}
	
	public void keyPressed(KeyEvent e){
		//System.out.println(e.getKeyCode());
		if(last == null)
		{
			changeInputEventList(e);
			last = e;
		}
		else
		{
			if(last.getKeyCode() != e.getKeyCode())
			{
				changeInputEventList(e);
				last = e;
			}
		}
	}
	public void keyReleased(KeyEvent e){
		changeInputEventList(e);
		if(last != null)
		{
			if(last.getKeyCode() == e.getKeyCode())
				last = null;
		}
	}
	public void keyTyped(KeyEvent e){}
	
	public void mousePressed(MouseEvent e){changeInputEventList(e);}
	public void mouseClicked(MouseEvent e){}
	public void mouseReleased(MouseEvent e){changeInputEventList(e);}
	public void mouseEntered(MouseEvent e){}
	public void mouseExited(MouseEvent e){}
	public void mouseMoved(MouseEvent e){changeInputEventList(e);}
	public void mouseDragged(MouseEvent e){changeInputEventList(e);}
	
	public static void main(String[] args){
		Game g = new Game();
		g.run();
	}
}
